/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package idea.sfo.viewer.graphics;


import java.awt.Color;
import java.util.NoSuchElementException;


/**
 * Implementation of the Fenestra Color Specification, defined here:
 * https://censuswiki.fenestra.com/bin/view/Main/ColorSpec
 * <p/>
 * Color name & sRGB pairs match the CSS 2.1 spec defined here:
 * http://www.w3.org/TR/CSS21/syndata.html#color-units
 * <p/>
 * @author renke002
 */
public class ColorSpec
{

    /*
     ************************COLOR--NAMES***************************************
     */
    /**
     * Keyword identifying the CSS 2.1 spec 'aqua' color.
     */
    public final static String COLOR_NAME_AQUA = "aqua";
    /**
     * Keyword identifying the CSS 2.1 spec 'black' color.
     */
    public final static String COLOR_NAME_BLACK = "black";
    /**
     * Keyword identifying the CSS 2.1 spec 'blue' color.
     */
    public final static String COLOR_NAME_BLUE = "blue";
    /**
     * Keyword identifying the CSS 2.1 spec 'fuchsia' color.
     */
    public final static String COLOR_NAME_FUCHSIA = "fuchsia";
    /**
     * Keyword identifying the CSS 2.1 spec 'gray' color.
     */
    public final static String COLOR_NAME_GRAY = "gray";
    /**
     * Keyword identifying the CSS 2.1 spec 'green' color.
     */
    public final static String COLOR_NAME_GREEN = "green";
    /**
     * Keyword identifying the CSS 2.1 spec 'lime' color.
     */
    public final static String COLOR_NAME_LIME = "lime";
    /**
     * Keyword identifying the CSS 2.1 spec 'maroon' color.
     */
    public final static String COLOR_NAME_MAROON = "maroon";
    /**
     * Keyword identifying the CSS 2.1 spec 'navy' color.
     */
    public final static String COLOR_NAME_NAVY = "navy";
    /**
     * Keyword identifying the CSS 2.1 spec 'olive' color.
     */
    public final static String COLOR_NAME_OLIVE = "olive";
    /**
     * Keyword identifying the CSS 2.1 spec 'orange' color.
     */
    public final static String COLOR_NAME_ORANGE = "orange";
    /**
     * Keyword identifying the CSS 2.1 spec 'purple' color.
     */
    public final static String COLOR_NAME_PURPLE = "purple";
    /**
     * Keyword identifying the CSS 2.1 spec 'red' color.
     */
    public final static String COLOR_NAME_RED = "red";
    /**
     * Keyword identifying the CSS 2.1 spec 'silver' color.
     */
    public final static String COLOR_NAME_SILVER = "silver";
    /**
     * Keyword identifying the CSS 2.1 spec 'teal' color.
     */
    public final static String COLOR_NAME_TEAL = "teal";
    /**
     * Keyword identifying the CSS 2.1 spec 'white' color.
     */
    public final static String COLOR_NAME_WHITE = "white";
    /**
     * Keyword identifying the CSS 2.1 spec 'yellow' color.
     */
    public final static String COLOR_NAME_YELLOW = "yellow";
    /**
     * A form-background color specification indicates that the item should be
     * rendered using the default form background color.
     */
    public final static String COLOR_NAME_FORM_BG = "form-background";
    /**
     * A transparent color specification indicates that the item in question
     * should not be rendered at all; whatever lies "underneath" the item should
     * show through.
     */
    public final static String COLOR_NAME_TRANSPARENT = "transparent";
    /*
     ************************COLOR*RGB*IDENTIFIERS******************************
     */
    /**
     * sRGB string identifying the CSS 2.1 spec 'aqua' color.
     */
    public final static String COLOR_SRGB_AQUA = "#00ffff";
    /**
     * sRGB string identifying the CSS 2.1 spec 'black' color.
     */
    public final static String COLOR_SRGB_BLACK = "#000000";
    /**
     * sRGB string identifying the CSS 2.1 spec 'blue' color.
     */
    public final static String COLOR_SRGB_BLUE = "#0000ff";
    /**
     * sRGB string identifying the CSS 2.1 spec 'fuchsia' color.
     */
    public final static String COLOR_SRGB_FUCHSIA = "#ff00ff";
    /**
     * sRGB string identifying the CSS 2.1 spec 'gray' color.
     */
    public final static String COLOR_SRGB_GRAY = "#808080";
    /**
     * sRGB string identifying the CSS 2.1 spec 'green' color.
     */
    public final static String COLOR_SRGB_GREEN = "#008000";
    /**
     * sRGB string identifying the CSS 2.1 spec 'lime' color.
     */
    public final static String COLOR_SRGB_LIME = "#00ff00";
    /**
     * sRGB string identifying the CSS 2.1 spec 'maroon' color.
     */
    public final static String COLOR_SRGB_MAROON = "#800000";
    /**
     * sRGB string identifying the CSS 2.1 spec 'navy' color.
     */
    public final static String COLOR_SRGB_NAVY = "#000080";
    /**
     * sRGB string identifying the CSS 2.1 spec 'olive' color.
     */
    public final static String COLOR_SRGB_OLIVE = "#808000";
    /**
     * sRGB string identifying the CSS 2.1 spec 'orange' color.
     */
    public final static String COLOR_SRGB_ORANGE = "#ffA500";
    /**
     * sRGB string identifying the CSS 2.1 spec 'purple' color.
     */
    public final static String COLOR_SRGB_PURPLE = "#800080";
    /**
     * sRGB string identifying the CSS 2.1 spec 'red' color.
     */
    public final static String COLOR_SRGB_RED = "#ff0000";
    /**
     * sRGB string identifying the CSS 2.1 spec 'silver' color.
     */
    public final static String COLOR_SRGB_SILVER = "#c0c0c0";
    /**
     * sRGB string identifying the CSS 2.1 spec 'teal' color.
     */
    public final static String COLOR_SRGB_TEAL = "#008080";
    /**
     * sRGB string identifying the CSS 2.1 spec 'white' color.
     */
    public final static String COLOR_SRGB_WHITE = "#ffffff";
    /**
     * sRGB string identifying the CSS 2.1 spec 'yellow' color.
     */
    public final static String COLOR_SRGB_YELLOW = "#ffff00";
    /**
     * Default form background color for electronic media.
     */
    public final static String COLOR_SRGB_FORM_BG_ELEC = "#C3DCE4";
    /**
     * Default form background color for paper.
     */
    public final static String COLOR_SRGB_FORM_BG_PAPER = "#D4EBE7";
    /*
     ***************************************************************************
     */
    /**
     * Keyword identifying the beginning of a 3-integer tuple defining a sRGB
     * color in functional notation.
     */
    public final static String KEYWORD_FUNC_BEGIN = "(";
    /**
     * Keyword identifying the end of a 3-integer tuple defining a sRGB color in
     * functional notation.
     */
    public final static String KEYWORD_FUNC_END = ")";
    /**
     * Keyword identifying a sRGB color in functional notation (3-integer
     * tuple).
     */
    public final static String KEYWORD_FUNC_SRGB = "rgb";
    /**
     * Keyword identifying a sRGB color in functional notation, defined as
     * percentage of the component's value.
     */
    public final static String KEYWORD_FUNC_SRGB_PERCENT = "%";
    /**
     * Keyword identifying the separator between values of a sRGB color in
     * functional notation.
     */
    public final static String KEYWORD_FUNC_SEPARATOR = ",";
    /**
     * Keyword identifying a sRGB color in hexadecimal notation.
     */
    public final static String KEYWORD_HEX_SRGB = "#";
    /**
     * A java.awt.Color representation of this object's color.
     */
    private Color color;


    /**
     * Create a new color based on the Fenestra Color Specification defined on
     * the <a
     * href="https://censuswiki.fenestra.com/bin/view/Main/ColorSpec">fenestra
     * wiki</a>.
     * <p/>
     * The specification of absolute sRGB colors in GIDS Suites adheres to the
     * Cascading Style Sheets (CSS), version 2.1, specification. Colors may be
     * specified as 6- or 3-digit hexadecimal values, three-integer tuples, or
     * via textual names.
     * <p/>
     * @param colorStr string to be parsed as color.
     * <p/>
     * @throws IllegalArgumentException if the color is not in RGB format or its
     *                                  values cannot be parsed as such.
     * <p/>
     * @throws NoSuchElementException   if the color name cannot be resolved to
     *                                  one of those supported by the system.
     * <p/>
     * @throws NumberFormatException    if the values cannot be parsed as
     *                                  numbers.
     */
    public ColorSpec( String colorStr )
            throws IllegalArgumentException , NoSuchElementException ,
            NumberFormatException
    {
        this.color = ColorSpec.parseColorString( colorStr );
    }


    /**
     * Convert a string containing a functional RGB definition to the
     * corresponding java.awt.Color.
     * <p/>
     * The format of an RGB value in the functional notation is 'rgb(' followed
     * by a comma-separated list of three numerical values (either three integer
     * values or three percentage values) followed by ')'.
     * <p/>
     * The integer value 255 corresponds to 100%, and to F or FF in the
     * hexadecimal notation: rgb(255,255,255) = rgb(100%,100%,100%) = #FFF.
     * White space characters are allowed around the numerical values.
     * <p/>
     * @param rgb string defining a color in functional notation.
     * <p/>
     * @return a java.awt.Color representation of the color passed as parameter.
     * <p/>
     * @throws IllegalArgumentException if the parameter is not in functional
     *                                  notation.
     * <p/>
     * @throws NumberFormatException    if the values cannot be parsed as
     *                                  numbers.
     */
    public static Color functionalToColor( String rgb ) throws
            IllegalArgumentException , NumberFormatException
    {
        // 
        rgb = rgb.replace( ColorSpec.KEYWORD_FUNC_BEGIN , "" ).replace( 
                ColorSpec.KEYWORD_FUNC_END , "" );

        if ( rgb.contains( ColorSpec.KEYWORD_FUNC_SRGB_PERCENT ) )
        {
            /*
             * if rgb format: ( rrr% , ggg% , bbb% ) then Color(float r, float
             * g, float b) Creates an opaque sRGB color with the specified red,
             * green, and blue values in the range (0.0 - 1.0).
             */
            String[] rgbVals = rgb.split( ColorSpec.KEYWORD_FUNC_SEPARATOR );


            float compRed = Float.parseFloat( rgbVals[0] ) / 100;
            float compGreen = Float.parseFloat( rgbVals[1] ) / 100;
            float compBlue = Float.parseFloat( rgbVals[2] ) / 100;

            return new Color( compRed , compGreen , compBlue );
        }
        else
        {
            /*
             * if rgb format: ( rrr , ggg , bbb ) then Color(int r, int g, int
             * b) Creates an opaque sRGB color with the specified red, green,
             * and blue values in the range (0 - 255).
             */
            String[] rgbVals = rgb.split( ColorSpec.KEYWORD_FUNC_SEPARATOR );

            return new Color(
                    Integer.parseInt( rgbVals[0] ) ,
                    Integer.parseInt( rgbVals[1] ) ,
                    Integer.parseInt( rgbVals[2] ) );
        }
    }


    /**
     *
     * @return this object's color.
     */
    public Color getColor()
    {
        return this.color;
    }


    public static Color getDefaultBackgroundColor()
    {
        return Color.decode( ColorSpec.COLOR_SRGB_FORM_BG_PAPER );
    }


    /**
     * Convert a color name (as defined in the CSS 2.1 spec) to the
     * corresponding java.awt.color.
     * <p/>
     * NOTE: Although color names in CSS 2.1 are case-insensitive, color names
     * in GIDS Suite are case-sensitive.
     * <p/>
     * @param name name of a color as defined in the CSS 2.1 spec.
     * <p/>
     * @return java.awt.Color representation of the color passed in as
     *         parameter.
     * <p/>
     * @throws NoSuchElementException if the name does not match one of those
     *                                supported.
     */
    public static Color nameToColor( String name ) throws NoSuchElementException
    {
        Color match;
        /*
         */
        switch ( name )
        {
            case ColorSpec.COLOR_NAME_AQUA:
                /*
                 * Keyword identifying the CSS 2.1 spec 'aqua' color.
                 */
                match = Color.decode( ColorSpec.COLOR_SRGB_AQUA );
                break;
            case ColorSpec.COLOR_NAME_BLACK:
                /*
                 * Keyword identifying the CSS 2.1 spec 'black' color.
                 */
                match = Color.decode( ColorSpec.COLOR_SRGB_BLACK );
                break;
            case ColorSpec.COLOR_NAME_BLUE:
                /*
                 * Keyword identifying the CSS 2.1 spec 'blue' color.
                 */
                match = Color.decode( ColorSpec.COLOR_SRGB_BLUE );
                break;
            case ColorSpec.COLOR_NAME_FUCHSIA:
                /*
                 * Keyword identifying the CSS 2.1 spec 'fuchsia' color.
                 */
                match = Color.decode( ColorSpec.COLOR_SRGB_FUCHSIA );
                break;
            case ColorSpec.COLOR_NAME_GRAY:
                /*
                 * Keyword identifying the CSS 2.1 spec 'gray' color.
                 */
                match = Color.decode( ColorSpec.COLOR_SRGB_GRAY );
                break;
            case ColorSpec.COLOR_NAME_GREEN:
                /*
                 * Keyword identifying the CSS 2.1 spec 'green' color.
                 */
                match = Color.decode( ColorSpec.COLOR_SRGB_GREEN );
                break;
            case ColorSpec.COLOR_NAME_LIME:
                /*
                 * Keyword identifying the CSS 2.1 spec 'lime' color.
                 */
                match = Color.decode( ColorSpec.COLOR_SRGB_LIME );
                break;
            case ColorSpec.COLOR_NAME_MAROON:
                /*
                 * Keyword identifying the CSS 2.1 spec 'maroon' color.
                 */
                match = Color.decode( ColorSpec.COLOR_SRGB_MAROON );
                break;
            case ColorSpec.COLOR_NAME_NAVY:
                /*
                 * Keyword identifying the CSS 2.1 spec 'navy' color.
                 */
                match = Color.decode( ColorSpec.COLOR_SRGB_NAVY );
                break;
            case ColorSpec.COLOR_NAME_OLIVE:
                /*
                 * Keyword identifying the CSS 2.1 spec 'olive' color.
                 */
                match = Color.decode( ColorSpec.COLOR_SRGB_OLIVE );
                break;
            case ColorSpec.COLOR_NAME_ORANGE:
                /*
                 * Keyword identifying the CSS 2.1 spec 'orange' color.
                 */
                match = Color.decode( ColorSpec.COLOR_SRGB_ORANGE );
                break;
            case ColorSpec.COLOR_NAME_PURPLE:
                /*
                 * Keyword identifying the CSS 2.1 spec 'purple' color.
                 */
                match = Color.decode( ColorSpec.COLOR_SRGB_PURPLE );
                break;
            case ColorSpec.COLOR_NAME_RED:
                /*
                 * Keyword identifying the CSS 2.1 spec 'red' color.
                 */
                match = Color.decode( ColorSpec.COLOR_SRGB_RED );
                break;
            case ColorSpec.COLOR_NAME_SILVER:
                /*
                 * Keyword identifying the CSS 2.1 spec 'silver' color.
                 */
                match = Color.decode( ColorSpec.COLOR_SRGB_SILVER );
                break;
            case ColorSpec.COLOR_NAME_TEAL:
                /*
                 * Keyword identifying the CSS 2.1 spec 'teal' color.
                 */
                match = Color.decode( ColorSpec.COLOR_SRGB_TEAL );
                break;
            case ColorSpec.COLOR_NAME_WHITE:
                /*
                 * Keyword identifying the CSS 2.1 spec 'white' color.
                 */
                match = Color.decode( ColorSpec.COLOR_SRGB_WHITE );
                break;
            case ColorSpec.COLOR_NAME_YELLOW:
                /*
                 * Keyword identifying the CSS 2.1 spec 'yellow' color.
                 */
                match = Color.decode( ColorSpec.COLOR_SRGB_YELLOW );
                break;
            case ColorSpec.COLOR_NAME_FORM_BG:
                /*
                 * A form-background color specification indicates that the item
                 * should be rendered using the default form background color.
                 */
                /*
                 * NOTE: we're only working with paper for now, so the color
                 * will default to paper background.
                 */
                match = Color.decode( ColorSpec.COLOR_SRGB_FORM_BG_PAPER );
                break;
            case ColorSpec.COLOR_NAME_TRANSPARENT:
                /*
                 * A transparent color specification indicates that the item in
                 * question should not be rendered at all; whatever lies
                 * "underneath" the item should show through.
                 */
                match = null;
                break;
            default:
                throw new NoSuchElementException( 
                        "The color " + name + " is not supported by the system." );
        }
        return match;
    }


    /**
     * Parse the string passed in as parameter and attempt to create a
     * java.awt.Color from it.
     * <p/>
     * @param colorStr string representing a color.
     * <p/>
     * @return a java.awt.Color object as defined in the parameter.
     * <p/>
     * @throws IllegalArgumentException if the color is not in RGB format or its
     *                                  values cannot be parsed as such.
     * <p/>
     * @throws NoSuchElementException   if the color name cannot be resolved to
     *                                  one of those supported by the system.
     * <p/>
     * @throws NumberFormatException    if the values cannot be parsed as
     *                                  numbers.
     */
    public static Color parseColorString( String colorStr )
            throws IllegalArgumentException , NoSuchElementException ,
            NumberFormatException
    {
        Color c;
        // 
        if ( colorStr.startsWith( ColorSpec.KEYWORD_FUNC_SRGB ) )
        {
            // rgb( rr , gg , bb )
            c = ColorSpec.functionalToColor(
                    colorStr.replace( ColorSpec.KEYWORD_FUNC_SRGB , "" ) );
        }
        else if ( colorStr.startsWith( ColorSpec.KEYWORD_HEX_SRGB ) )
        {
            // #FFFFF
            c = ColorSpec.rgbStringToColor( colorStr );
        }
        else
        {
            // let's assume it's a named color
            c = ColorSpec.nameToColor( colorStr );
        }
        return c;
    }


    /**
     * Converts a string containing a color description (in # RR GG BB format)
     * into a java.awt.color object.
     * <p/>
     * @param sRGBcolor a string representing a color in #RGB format
     * @return a new Color object
     * @throws NumberFormatException if the string cannot be interpreted as a
     *                               color
     */
    public static Color rgbStringToColor( String sRGBcolor ) throws
            NumberFormatException
    {
        return Color.decode( sRGBcolor );
    }


    /**
     * Sets this object's color to the one passed as parameter.
     * <p/>
     * @param c the new color.
     */
    public void setColor( Color c )
    {
        this.color = c;
    }


    /**
     * Sets this object's color to the one passed as parameter.
     * <p/>
     * @param colorStr
     * <p/>
     * @throws IllegalArgumentException if the color is not in RGB format or its
     *                                  values cannot be parsed as such.
     * <p/>
     * @throws NoSuchElementException   if the color name cannot be resolved to
     *                                  one of those supported by the system.
     * <p/>
     * @throws NumberFormatException    if the values cannot be parsed as
     *                                  numbers.
     */
    public void setColor( String colorStr )
            throws IllegalArgumentException , NoSuchElementException ,
            NumberFormatException
    {
        this.color = ColorSpec.parseColorString( colorStr );
    }

}
